kim.zhang

风在前,无惧!


  • 首页

  • 标签42

  • 分类12

  • 归档94

  • 搜索

发表于 2022-01-21 更新于 2022-01-23
本文字数: 8.4k 阅读时长 ≈ 8 分钟

AOP

关于动态代理,Spring中有两种实现方式:JDK动态代理,CGLIB代理.

JDK动态代理和CGLIB的区别:
JDK动态代理: 通过与代理类实现同一接口来实现代理.

  • 在Spring中注入的时候,应该使用接口注入,而不能使用实现类注入.
  • 在通过applicationContext来getBean的时候,不能通过类.class的方式获取bean.因为bean已经被JDK代理为Proxy类,不再是原始的类了.

CGLIB: 通过修改字节码来实现代理.

  • 在Spring中注入时,可以使用接口或实现类来注入.
  • 在通过applicationContext来getBean的时候,可以通过接口或实现类获取bean

Spring5中如果实现了接口,默认使用JDK动态代理,如果继承类使用CGLIB代理.
SpringBoot2.0中默认使用CGLIB代理.如果想使用JDK代理,可以设置proxy-target-class=false来强制使用JDK代理.

proxy-target-class这个属性值在以下几个地方存在,也就是以下几种情况下bean会被代理:
1.@EnableAspectJAutoProxy
开启AOP功能,如果bean的方法存在切面,bean会被代理.Springboot2.0中默认使用CGLIB代理.
可以在配置文件中设置spirng.aop.proxy-target-class=false来强制使用JDK代理.在@EnableAspectJAutoProxy(proxyTargetClass = false)在Springboot2.0中已经不生效.
如果在配置文件中设置spring.aop.auto=false,则AopAutoConfiguration自动配置不生效,此时如果使用@EnableAspectJAutoProxy则生效.代理方式取决于注解上指定的proxy-target-class的值.
2.@EnableCaching
3.@EnableTransactionManagement
开启事务功能

4.@EnableAsync:
如果类存在@Async,bean会被代理.代理方式取决于bean有没有实现接口.当然,可以通过@EnableAsync(proxyTargetClass = true)来设置使用@Async时强制使用CGLIB代理.

如果bean上同时存在切面和@Async.也就是@EnableAspectJAutoProxy和@EnableAsync同时生效,bean的代理方式取决于@EnableAspectJAutoProxy.因为在为@Async生成代理的时候,如果bean已经被代理了,则不再进行代理,直接将@Async加入切面即可.

proxy-target-class配置

在Springboot2.0中,proxy-target-class可以在多个地方进行设置:
1.配置文件中设置spring.aop.proxy-target-class
2.配置类上注解@EnableAspectJAutoProxy,@EnableCaching,@EnableTransactionManagement,@EnableAsync

那么,这么多地方可以设置proxy-target-class,谁的优先级比较高,最终proxy-target-class的值由谁决定呢?

@EnableCaching,@EnableTransactionManagement,这两个注解会导入AutoProxyRegister,而AutoProxyRegister类里面的registerBeanDefinitions方法,会扫描配置类上的注解,并向容器中注入InfrastructureAdvisorAutoProxyCreator.
当@EnableTransactionManagement没有在配置类上显示指定时,springboot自动配置TransactionAutoConfigutartion(即使配置文件中spring.aop.auto=false也会向容器注入TransactionAutoConfiguration配置类)会读取配置文件中spring.aop.proxy-target-class,bean的代理方式由配置文件中的值决定.

@EnableAsync比较特殊,它只是执行AsyncAnnotationBeanPostProcessor.而这个BeanPostProvessor的proxy-target-class只取决于@EnableAsync注解上的值.
当bean只存在@Async时,bean的代理方式完全由@EnableAsync注解上的proxy-target-class的值决定.
当bean存在@Async,又存在@Transactional时,bean的代理方式会被@EnableTransactionManagement上的proxy-target-class的值决定.

而@EnableAspectJAutoProxy会倒导入AspectJAutoProxyRegistrar,而AspectJAutoProxyRegistrar类里面的registerBeanDefinitions方法会向容器中注入AnnotationAwareAspectJAutoProxyCreator.
配置文件中的spring.aop.auto=true相当于开启@EnableAspectJAutoProxy.如果配置文件中spring.aop.auto=true,则AopAutoConfiguration自动配置类会生效.相当于也会向容器中注入AnnotationAwareAspectJAutoProxyCreator.
在注入AnnotationAwareAspectJAutoProxyCreator后,会读取标记了注解@EnableAspectJAutoProxy上的proxy-target-class的值,不管是注解方式还是在配置文件中配置,只要有一个proxy-target-class的值是true,bean的代理方式就会使用CGLIB代理.

如果同时存在@EnableTransactionManagement|@EnableCaching|@EnableAspectJAutoProxy上,proxy-target-class的值由@EnableAspectJAutoProxy决定.
因为在AopConfigUtils类的静态代码块中,规定了优先级,AnnotationAwareAspectJAutoProxyCreator总是会覆盖前面的ProxyCreator.

1
2
3
4
5
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}

如果spring.aop.auto=false,也没有@EnableAspectJAutoProxy. 那么,只要有@EnableCaching|@EnableTransactionManagement中的proxy-target-class有一个值为true,就使用CGLIB代理,因为AutoProxyRegister扫描到一个proxy-target-class的值为ture,就默认使用CGLIB代理了,而不会继续扫描第二个注解的proxy-target-class的值.

综上所述,如果一个bean只有@Async,bean的代理方式由@EnableAsync的proxy-target-class决定.
如果一个bean有事务,有AOP切面,bean的代理方式由@EnableAspectJAutoProxy决定(如果配置文件中spring.aop.auto=true,则bean的代理方式由配置文件中spring.aop.proxy-target-class的值决定).而在Spirngboot2.0中,spring.aop.proxy-target-class默认值是true,bean默认是CGLIB代理.

如果配置文件中spring.aop.auto=true,默认使用CGLIB代理.proxy-target-class的最终取值取决于两者,只要配置文件或配置类中有一个地方设置proxy-target-class=true,就使用CGLIB代理.只有两个地方都设置proxy-target-calss=fasle才使用JDK动态代理.
如果配置文件中spring.aop.auto=false,proxy-target-class的值仅仅受注解配置影响.如果存在多个@Enablexxx注解,只要有一个proxy-target-classd的值为ture,就默认使用CGLIB代理.

proxy-target-class的设置

@EnableTransactionManagement

@EnableTransactionManagement默认使用JDK动态代理,导入了TransactionManagementConfigurationSelector.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {

// 默认使用JDK动态代理,如果proxyTargetClass=true则使用CGLIB代理
boolean proxyTargetClass() default false;

// AdviceMode有PROXY和ASPJECTJ两种,默认是PROXY
AdviceMode mode() default AdviceMode.PROXY;


int order() default Ordered.LOWEST_PRECEDENCE;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {

/*
根据@EnableTransactionManagement指定的mode模式导入配置类.
mode默认是PROXY,此时会导入AutoProxyRegistrar和ProxyTransactionManagementConfiguration两个配置类
*/
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
}

AutoProxyRegistrar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {

private final Log logger = LogFactory.getLog(getClass());

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
// 获取启动类上所有的注解
Set<String> annTypes = importingClassMetadata.getAnnotationTypes();
for (String annType : annTypes) {
// 循环每个注解,获取每个注解的所有属性
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annType);
if (candidate == null) {
continue;
}
// 所有和代理有关的注解都会有mode和proxyTargetClass的属性
// 如果mode是PROXY的模式,创建InfrastructureAdvisorAutoProxyCreator,并向容器中导入internalAutoProxyCreator
// 如果proxyTargetClass=true,从容器中获取internalAutoProxyCreator,并设置proxyTargetClass=true,强制使用CGLIB代理
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
// 如果有多个proxy-target-class,只要找到第一个proxy-target-class=true,就使用CGLIB代理了,其他的proxy-target-class不再生效
if (mode == AdviceMode.PROXY) {
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound && logger.isInfoEnabled()) {
String name = getClass().getSimpleName();
logger.info(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
}

ProxyTransactionManagementConfiguration:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
@Configuration(proxyBeanMethods = false)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {

@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
TransactionAttributeSource transactionAttributeSource, TransactionInterceptor transactionInterceptor) {

BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
advisor.setAdvice(transactionInterceptor);
if (this.enableTx != null) {
advisor.setOrder(this.enableTx.<Integer>getNumber("order"));
}
return advisor;
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource);
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager);
}
return interceptor;
}

}
一毛也是爱~
Kim.Zhang 微信支付

微信支付

  • 文章目录
  • 站点概览
Kim.Zhang

Kim.Zhang

且行且珍惜
94 日志
12 分类
42 标签
E-Mail Weibo
  1. 1. AOP
  2. 2. proxy-target-class配置
  3. 3. @EnableTransactionManagement
粵ICP备19091267号 © 2019 – 2022 Kim.Zhang | 629k | 9:32
本站总访问量 4 次 | 有 309 人看我的博客啦 |
博客全站共176.7k字
载入天数...载入时分秒...
0%